import { Hono } from 'hono';
import { basicAuth } from 'hono/basic-auth';
import { etag } from 'hono/etag';
import { poweredBy } from 'hono/powered-by';
import { prettyJSON } from 'hono/pretty-json';
import { XMLParser } from 'fast-xml-parser';
import { Env } from './types';
import { getToken } from './wechat';
import { sha1 } from './utils';
import { textMessage } from './template';
import replyCallback from './reply';
import { extractVideo } from './extract';

const app = new Hono<{ Bindings: Env }>();
const xmlParser = new XMLParser();

// Mount Builtin Middleware
app.use('*', poweredBy());
// app.use('*', logger())
app.use(
	'/auth/*',
	basicAuth({
		username: 'hono',
		password: 'acoolproject',
	})
);
app.use('/etag/*', etag());

// Custom Middleware
// Add Custom Header
app.use('/hello/*', async (c, next) => {
	await next();
	c.header('X-message', 'This is addHeader middleware!');
});

// Add X-Response-Time header
app.use('*', async (c, next) => {
	const start = Date.now();
	await next();
	const ms = Date.now() - start;
	c.header('X-Response-Time', `${ms}ms`);
});

// Custom Not Found Message
app.notFound(c => {
	return c.text('Custom 404 Not Found', 404);
});

// Error handling
app.onError((err, c) => {
	console.error(`${err}`);
	return c.text('Custom Error Message', 500);
});

// Routing
app.get('/', c => c.text('Hono!!'));
// Use Response object directly
app.get('/hello', () => new Response('This is /hello'));

// Named parameter
app.get('/entry/:id', c => {
	const id = c.req.param('id');
	return c.text(`Your ID is ${id}`);
});

// Redirect
app.get('/redirect', c => c.redirect('/'));
// Authentication required
app.get('/auth/*', c => c.text('You are authorized'));
// ETag
app.get('/etag/cached', c => c.text('Is this cached?'));

// Async
app.get('/fetch-url', async c => {
	const response = await fetch('https://example.com/');
	return c.text(`https://example.com/ is ${response.status}`);
});

// Request headers
app.get('/user-agent', c => {
	const userAgent = c.req.header('User-Agent');
	return c.text(`Your UserAgent is ${userAgent}`);
});

// JSON
app.get('/api/posts', prettyJSON(), c => {
	const posts = [
		{ id: 1, title: 'Good Morning' },
		{ id: 2, title: 'Good Afternoon' },
		{ id: 3, title: 'Good Evening' },
		{ id: 4, title: 'Good Night' },
	];
	return c.json(posts);
});
// status code
app.post('/api/posts', c => c.json({ message: 'Created!' }, 201));
// default route
app.get('/api/*', c => c.text('API endpoint is not found', 404));

app.get('/wechat/token', async c => {
	const token = await getToken(c.env);
	return c.json(token);
});

app.all('/wechat/callback', async c => {
	if (c.req.method === 'GET') {
		const signature = c.req.query('signature');
		const echostr = c.req.query('echostr');
		const timestamp = c.req.query('timestamp');
		const nonce = c.req.query('nonce');
		let oriArray = [nonce, timestamp, c.env.APP_TOKEN];
		oriArray.sort();
		const original = oriArray.join('');
		let scyptoString = await sha1(original);
		if (signature == scyptoString) {
			return c.body(echostr || 'error');
		} else {
			return c.body('error');
		}
	}
	if (c.req.method === 'POST') {
		const body = await c.req.text();
		const msg = xmlParser.parse(body).xml;
		console.log('msg:', msg);
		const Content = msg.Content;
		let reply = await replyCallback(Content, c.env);
		switch (msg.MsgType) {
			case 'text':
				// 封装要回复的消息参数
				let message = {
					FromUserName: msg.FromUserName,
					ToUserName: msg.ToUserName,
					reply: reply,
				};
				return c.body(textMessage(message));

			default:
				return c.body(''); // 不是文本消息是默认响应一个空
		}
	}
	return c.body('ok');
});

app.post('/extract', async c => {
	const body = await c.req.json();
	const text = await extractVideo(body.input);
	return c.text(text);
});

app.get('/cache/rss', async c => {
	const list = await c.env.KV.list();
	const data = await Promise.all(list.keys.map(item => c.env.KV.get(item.name)))
	return c.json(data);
});

// Throw Error
app.get('/error', () => {
	throw Error('Error has occurred');
});

// @ts-ignore
app.get('/type-error', () => 'return not Response instance');

export default app;
